package common;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;

import math.AbstractEstimateIterMin;
import math.LeastAbsMethodIterLS;
import math.LeastAbsMethodIterMin;
import math.LeastSquareMethod;
import math.WLeastAbsMethodIterMin;
import math.common.MathCommon;
import math.probability.ExpDistribution;
import math.probability.LNormalDistribution;
import math.probability.NormalDistribution;
import math.probability.ProbDistribution;
import math.probability.UniformDistribution;

public class testLAM {
	
	public void LAM_var_rndX () {
		int nSet[] = {50,100, 200, 300, 500, 1000};
		
		List<Double> LAM_est = new ArrayList<Double>(); 

		Double sigma = 1.0;
		Double F0 = 1.0/Math.sqrt(2*Math.PI*sigma);
		ProbDistribution err_dist = new NormalDistribution(0.0, sigma);
//		ProbDistribution u_dist = new UniformDistribution(1.0,10.0);
		ProbDistribution u_dist = new ExpDistribution(0.75);
		AbstractEstimateIterMin LAM_method = new LeastAbsMethodIterLS();
		
		for (int n : nSet) {
			LAM_est.clear();
			int M = 5000;
			List<Double> V = new ArrayList<Double>();
			Double B=0.0;
			for (int i = 0; i < M; i++) {
				V.clear();
				List<Double> U = u_dist.getSample(n);
				for (int j=0; j<n; j++) {
					B += U.get(j)*U.get(j);
				}
				for (int j=0; j<n; j++) {
					V.add(2.0 * U.get(j) + err_dist.getNext());
				}
				LAM_est.add(LAM_method.estimate(V, U));
				
				
				
			}
			Double var = MathCommon.getVar(LAM_est);
			List<Double> U = u_dist.getSample(n);
			
			
			Double var_est = 1.0/(4*F0*F0*(B/M));
		
			System.out.println ((B/n/M) + " " + n + "  " + MathCommon.getMean(LAM_est) + " " + var  + "  " 
					+ var_est + "  " + (Math.abs(var_est-var)/var)*100 );

		}
	}
		
		public void LAM_var_detX () {
			int nSet[] = {100, 200, 500, 1000};
			int mSet[] = { 5000};		
			
					List<Double> b1 = new ArrayList<Double>(); 
					List<Double> b2 = new ArrayList<Double>(); 
					List<Double> b3 = new ArrayList<Double>(); 
					
					Double b = 2.0;

					Double sigma = 0.5;
					Double F0 = 1.0/Math.sqrt(2*Math.PI*sigma);
					ProbDistribution err_dist = new NormalDistribution(0.0, sigma);
//					ProbDistribution u_dist = new UniformDistribution(1.0,10.0);
//					ProbDistribution u_dist = new NormalDistribution(5.0,10.0);
					ProbDistribution u_dist = new ExpDistribution(0.75);
					
					System.out.println ("n    b1     b2    b3    var1     var2   var3    var_est");
//					System.out.println ("n    b1     b2    err1     err2");
					
					Random r = new Random();
					
					for (int n : nSet) {
						b1.clear();
						b2.clear();
						b3.clear();
						r.setSeed(System.currentTimeMillis());
						//int M = 5000;
						List<Double> U = u_dist.getSample(n,r);
						Double B=0.0;
						Double B6=0.0;
						Double B4=0.0;
						Double D = 0.0;
						Double meanU = MathCommon.getMean(U);
						for (int j=0; j<n; j++) {
							B += Math.pow(U.get(j),2.0);
							B6 += Math.pow(U.get(j),6.0);
							B4 += Math.pow(U.get(j),4.0);
						}
						 for (int M : mSet) {
						List<Double> V = new ArrayList<Double>();
						for (int i = 0; i < M; i++) {
							V.clear();
							for (int j=0; j<n; j++) {
								V.add(b * U.get(j) + err_dist.getNext(r));
							}
							Double LAM = new LeastAbsMethodIterMin(1.0/(10.0*n)).estimate(V, U);
							Double WLAM = new WLeastAbsMethodIterMin(1.0/(10.0*n)).estimate(V, U);
							Double e2 = (WLAM-b)*(B4/Math.sqrt(B6));
							Double e1 = (LAM-b)*Math.sqrt(B);
//							Double e3 = (new LeastSquareMethod().estimate(V, U)-b)*Math.sqrt(B);
							Double e3 = (LAM-WLAM)/Math.sqrt(B6/Math.pow(B4,2.0)-1.0/B);
							b1.add(e1);
							b2.add(e2);
							b3.add(e3);
							
							int n1 = n-2;
							int k1 = (int) Math.round((n1+1)/2.0 - Math.sqrt(n1)); 
							int k2 = (int) Math.round((n1+1)/2.0 + Math.sqrt(n1));
							List<Double> res = new ArrayList<Double> ();
							for (int l=0; l<n; l++) {
								res.add(V.get(l)-U.get(l)*WLAM);
							}
							Collections.sort(res);
							D += n1*Math.pow((res.get(k2-1)-res.get(k1-1))/4.0,2.0);
							
						}
						Double var1 = MathCommon.getVar(b1);
						Double var2 = MathCommon.getVar(b2);
						Double var3 = MathCommon.getVar(b3);
						Double var_est = (Math.PI*sigma)/2.0;
						
						System.out.printf("%d  %.5f  %.5f  %.5f  %.5f  %.5f  %.5f  %.5f\n", n, 
								MathCommon.getMean(b1), MathCommon.getMean(b2), MathCommon.getMean(b3),
								var1, var2, var3, (double) D/M);

					}
					}
				}
		
		public void testDispFormula () {
			// Check relation D[WLAM-LAM]=D[WLAM]-D[LAM]
			int nSet[] = {500, 1000};
			int M = 5000;	
			
					List<Double> b1 = new ArrayList<Double>(); 
					List<Double> b2 = new ArrayList<Double>(); 
					List<Double> b3 = new ArrayList<Double>(); 
					
					
					
					Double b = 3.0;
					Double b0 = 0.0;
					
					ProbDistribution distEps = new NormalDistribution(0.0, 0.3);
					ProbDistribution distXi = new NormalDistribution(0.0, 0.0);
					ProbDistribution distX = new ExpDistribution(0.8);
//					ProbDistribution distX = new LNormalDistribution(-Math.log(3.0)/2.0, Math.log(3.0));
					
					
					
					System.out.println ("n    distX  distEps  D[WLAM-LAM]    D[WLAM]-D[LAM]");
//					System.out.println ("n    b1     b2    err1     err2");
					
					Random r = new Random();
					
					for (int n : nSet) {
						b1.clear();
						b2.clear();
						b3.clear();
						
						Double U2W = 0.0;
						Double U2 = 0.0;
						Double U4W = 0.0;
						for (int i = 0; i < M; i++) {
							StructuralModel model =  new StructuralModel(n,distX, distXi, distEps, b0, b);
							Map<Double, Double> data = MathCommon.Normalize(model.getData());
							List<Double> V = new ArrayList<Double> (data.values());
							List<Double> U = new ArrayList<Double> (data.keySet());
							
							for (Double u : U) {
								U2W += Math.pow(u, 4.0);
								U2 += Math.pow(u, 2.0);
								U4W += Math.pow(u, 6.0);
							}
							
							
							Double LAM = new LeastAbsMethodIterMin(1.0/(10.0*n)).estimate(V, U);
							Double WLAM = new WLeastAbsMethodIterMin(1.0/(10.0*n)).estimate(V, U);
							
							b1.add(LAM);
							b2.add(WLAM);
							b3.add(WLAM-LAM);
						}
						U2W = U2W/M;
						U2 = U2/M;
						U4W = U4W/M;
						
						Double var1 = MathCommon.getVar(b1);
						Double var2 = MathCommon.getVar(b2);
						Double var3 = MathCommon.getVar(b3);
						
						List<Double> b31 = new ArrayList<Double>();
						List<Double> b32 = new ArrayList<Double>();
						Double f02 = 1.0/(2*Math.PI*0.2);
						for (int i=0; i < M; i++) {
							b31.add(b3.get(i)/Math.sqrt(var3));
							b32.add(b3.get(i)/Math.sqrt(var2-var1));
							
						}
						
						

						
						System.out.printf("%d  %s  %s  %.5f  %.5f\n", n, distX.toString(), distEps.toString(),
								 MathCommon.getVar(b31),MathCommon.getVar(b32));

					}
					
				}
		
		
	
		public void LAM_est_HD () throws FileNotFoundException {
			
			//int n = 10;
			Double [] U = new Double [] {1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0};//,10.0};

			Double [] V = new Double [] {3.056052624,6.562662519,8.118907293,8.546210696,
					11.41937461,13.33805286,16.84032331,17.24947165,18.96354917};//,22.95345782};
			// true values a = 1, b = 2
			// LAD estimation b = 1.943365677, a = 1.702546227
			
			Double medU = MathCommon.quantile(Arrays.asList(U), 0.5);
			Double medV = MathCommon.quantile(Arrays.asList(V), 0.5);
			
			for (int i = 0; i < U.length; i++) {
				U[i] = U[i] - medU;
				V[i] = V[i] - medV;
			}
			
			
			
//			Vector b = new LeastAbsMethodIterMin().estimateFull( Arrays.asList(V), Arrays.asList(U) );
			Double b1 = new LeastAbsMethodIterMin().estimate( Arrays.asList(V), Arrays.asList(U) );
			Double b2 = new LeastAbsMethodIterLS().estimate( Arrays.asList(V), Arrays.asList(U) );
			
			System.out.println ("LAD Estimator:");
//			System.out.println(b.get(0) + ", " + b.get(1));
			System.out.println(b1);
			System.out.println(b2);
		}
		
		public void testDist() {
			Double sigma = 1.0;
			ProbDistribution err = new NormalDistribution(0.0, sigma);
//			ProbDistribution err = new UniformDistribution(0.0, 1.0);
			int M = 1000;
	
			int nSet [] = {100, 200, 300, 500, 1000, 10000, 100000};
			System.out.println ("Mean  Var");
			int s = 0;
			for (int n : nSet) {
				int p = 0;
				for (int j = 0; j < M; j++) { 
					List<Double> rnd = new ArrayList<Double>();
					//	rnd = err.getSample(n);
					for (int i = 0; i < n; i++) {
						rnd.add(err.getNext());
					}
					if (MathCommon.getMean(rnd) > 3.0/Math.sqrt(n))
						p++;
				}
				//s = n;
				System.out.printf("%d  %.5f\n", n, (double) p/n);
			}
			
		}
		

	public static void main (String [] arg) throws FileNotFoundException {
		testLAM test = new testLAM();
		test.testDispFormula ();
//		test.testDist();
	}
	
}
		

		

